package io.gitee.zhangbinhub.admin.component

import io.gitee.zhangbinhub.acp.boot.exceptions.WebException
import io.gitee.zhangbinhub.acp.boot.log.LogAdapter
import io.gitee.zhangbinhub.acp.core.common.CalendarTools
import io.gitee.zhangbinhub.acp.core.common.task.BaseAsyncTask
import io.gitee.zhangbinhub.acp.core.common.task.threadpool.ThreadPoolService
import io.gitee.zhangbinhub.admin.base.PendingCreatedNotify
import io.gitee.zhangbinhub.admin.base.PendingFinishedNotify
import io.gitee.zhangbinhub.admin.conf.WorkFlowCustomerConfiguration
import io.gitee.zhangbinhub.admin.constant.WorkFlowConstant
import io.gitee.zhangbinhub.admin.repo.UserRepository
import org.dromara.warm.flow.core.FlowEngine
import org.dromara.warm.flow.core.dto.DefJson
import org.dromara.warm.flow.core.entity.Instance
import org.dromara.warm.flow.core.entity.Task
import org.dromara.warm.flow.core.enums.NodeType
import org.dromara.warm.flow.core.listener.GlobalListener
import org.dromara.warm.flow.core.listener.ListenerVariable
import org.dromara.warm.flow.core.service.DefService
import org.dromara.warm.flow.core.service.InsService
import org.dromara.warm.flow.core.service.UserService
import org.joda.time.DateTime
import org.springframework.stereotype.Component

/**
 * 流程启动
 * start -》 assignment -》 finish -》 create( listenerVariable.getNextNodes().forEach )
 *
 * 办理人变更
 * start -》 入库 -》 finish
 *
 * 办理
 * start -》 assignment -》 入库 -》 finish -》 create( listenerVariable.getNextNodes().forEach )
 */
@Component
class WorkFlowGlobalListener(
    private val logAdapter: LogAdapter,
    private val workFlowCustomerConfiguration: WorkFlowCustomerConfiguration,
    private val workFlowPermissionHandler: WorkFlowPermissionHandler,
    private val pendingCreatedNotifyList: MutableList<PendingCreatedNotify>,
    private val pendingFinishedNotifyList: MutableList<PendingFinishedNotify>,
    private val defService: DefService,
    private val insService: InsService,
    private val userService: UserService,
    private val userRepository: UserRepository
) : GlobalListener {
    private fun begin(listenerVariable: ListenerVariable, newTask: Task) {
        val disList = newTask.userList.flatMap { workFlowPermissionHandler.userIdList(listOf(it.processedBy)) }
        FlowEngine.jsonConvert.strToBean(listenerVariable.instance.defJson, DefJson::class.java)?.let { defJson ->
            for (nodeJson in defJson.nodeList) {
                if (nodeJson.nodeCode == newTask.nodeCode) {
                    if (disList.isNotEmpty()) {
                        nodeJson.extMap[WorkFlowConstant.NODE_EXT_APPROVAL] =
                            disList.mapNotNull { userRepository.findById(it).orElse(null)?.name }
                                .joinToString(separator = ",")
                    }
                    if (nodeJson.extMap[WorkFlowConstant.NODE_EXT_START_TIME] == null) {
                        nodeJson.extMap[WorkFlowConstant.NODE_EXT_START_TIME] =
                            DateTime(newTask.createTime.time).toString(CalendarTools.DATETIME_FORMAT)
                    }
                }
            }
            listenerVariable.instance.setDefJson(FlowEngine.jsonConvert.objToStr(defJson))
        }
        insService.updateById(listenerVariable.instance)
        notifyPending(listenerVariable, newTask, listOf(), disList)
    }

    private fun complete(listenerVariable: ListenerVariable) {
        val oriList: List<String> = listenerVariable.task.userList
            ?.flatMap { workFlowPermissionHandler.userIdList(listOf(it.processedBy)) } ?: listOf()
        var disList: List<String> = listOf()
        if (listenerVariable.flowParams == null) {
            // 无跳转仅修改办理人时，通知新的办理人
            disList = userService.getByAssociateds(listOf(listenerVariable.task.id)).flatMap {
                workFlowPermissionHandler.userIdList(listOf(it.processedBy))
            }
            FlowEngine.jsonConvert.strToBean(listenerVariable.instance.defJson, DefJson::class.java)?.let { defJson ->
                for (nodeJson in defJson.nodeList) {
                    if (nodeJson.nodeCode == listenerVariable.node.nodeCode) {
                        if (disList.isNotEmpty()) {
                            nodeJson.extMap[WorkFlowConstant.NODE_EXT_APPROVAL] =
                                disList.mapNotNull { userRepository.findById(it).orElse(null)?.name }
                                    .joinToString(separator = ",")
                        } else {
                            nodeJson.extMap.remove(WorkFlowConstant.NODE_EXT_APPROVAL)
                        }
                    }
                }
                listenerVariable.instance.setDefJson(FlowEngine.jsonConvert.objToStr(defJson))
            }
        } else {
            FlowEngine.jsonConvert.strToBean(listenerVariable.instance.defJson, DefJson::class.java)?.let { defJson ->
                for (nodeJson in defJson.nodeList) {
                    if (nodeJson.nodeCode == listenerVariable.node.nodeCode) {
                        nodeJson.extMap[WorkFlowConstant.NODE_EXT_FINISH_TIME] =
                            DateTime(listenerVariable.instance.updateTime.time).toString(CalendarTools.DATETIME_FORMAT)
                        nodeJson.extMap[WorkFlowConstant.NODE_EXT_APPROVAL] =
                            userRepository.findById(listenerVariable.flowParams.handler).orElse(null)?.name
                    }
                }
                listenerVariable.instance.setDefJson(FlowEngine.jsonConvert.objToStr(defJson))
            }
        }
        insService.updateById(listenerVariable.instance)
        notifyPending(listenerVariable, listenerVariable.task, oriList, disList)
    }

    override fun finish(listenerVariable: ListenerVariable) {
        if (listenerVariable.node.nodeType == NodeType.START.key) {
            listenerVariable.nextTasks?.forEach { task ->
                if (task.nodeType == NodeType.BETWEEN.key) {
                    logAdapter.debug("收到工作流任务【${task.nodeName}】创建事件")
                    begin(listenerVariable, task)
                }
            }
        } else if (listenerVariable.node.nodeType == NodeType.BETWEEN.key && listenerVariable.task != null) {
            logAdapter.debug("收到工作流任务【${listenerVariable.task.nodeName}】完成事件")
            complete(listenerVariable)
            listenerVariable.nextTasks?.forEach { task ->
                if (task.nodeType == NodeType.BETWEEN.key) {
                    logAdapter.debug("收到工作流任务【${task.nodeName}】创建事件")
                    begin(listenerVariable, task)
                }
            }
        }
    }

    private fun notifyPending(
        listenerVariable: ListenerVariable,
        task: Task,
        orgList: List<String>,
        disList: List<String>
    ) {
        orgList.filterNot { disList.contains(it) }.let {
            if (it.isNotEmpty()) {
                notifyPendingFinished(listenerVariable.instance, task, it)
            }
        }
        disList.filterNot { orgList.contains(it) }.let {
            if (it.isNotEmpty()) {
                notifyPendingCreated(listenerVariable.instance, task, it)
            }
        }
    }

    /**
     * 生成待办通知
     * @param instance 流程实例
     * @param task  任务
     * @param userIdList 用户ID
     */
    @Throws(WebException::class)
    fun notifyPendingCreated(instance: Instance, task: Task, userIdList: List<String>) {
        if (workFlowCustomerConfiguration.notifyPendingCreated) {
            logAdapter.debug("进行待办通知...")
            logAdapter.debug("userIdList --> $userIdList")
            defService.getById(instance.definitionId)?.also { definition ->
                if (userIdList.isNotEmpty()) {
                    logAdapter.debug("【${definition.flowCode}】【${definition.flowName}】【instanceId:${instance.id}】【businessId:${instance.businessId}】【taskId:${task.id}】开始待办通知处理，通知处理器数量【${pendingCreatedNotifyList.size}】...")
                    logAdapter.debug("处理器：${pendingCreatedNotifyList}")
                    pendingCreatedNotifyList.forEach { handle ->
                        ThreadPoolService.getInstance(5, 5, Int.MAX_VALUE, "create-pending-pool")
                            .addTask(object : BaseAsyncTask("create-pending") {
                                override fun afterExecuteFun(result: Any) {}
                                override fun beforeExecuteFun(): Boolean = true
                                override fun executeFun(): Any {
                                    handle.doTaskNotify(definition, instance, task, userIdList)
                                    return true
                                }
                            })
                    }
                }
            }
                ?: throw WebException("获取流程定义失败！【instanceId:${instance.id}】【businessId:${instance.businessId}】【taskId:${task.id}】")
        }
    }

    /**
     * 完成待办通知
     * @param instance 流程实例
     * @param task  任务
     * @param userIdList 用户ID
     */
    @Throws(WebException::class)
    fun notifyPendingFinished(instance: Instance, task: Task, userIdList: List<String>) {
        if (workFlowCustomerConfiguration.notifyPendingFinished) {
            logAdapter.debug("删除待办通知...")
            logAdapter.debug("userIdList --> $userIdList")
            defService.getById(instance.definitionId)?.also { definition ->
                if (userIdList.isNotEmpty()) {
                    logAdapter.debug("【${definition.flowCode}】【${definition.flowName}】【instanceId:${instance.id}】【businessId:${instance.businessId}】【taskId:${task.id}】开始删除待办通知处理，通知处理器数量【${pendingFinishedNotifyList.size}】...")
                    logAdapter.debug("处理器：${pendingFinishedNotifyList}")
                    pendingFinishedNotifyList.forEach { handle ->
                        ThreadPoolService.getInstance(5, 5, Int.MAX_VALUE, "delete-pending-pool")
                            .addTask(object : BaseAsyncTask("delete-pending") {
                                override fun afterExecuteFun(result: Any) {}
                                override fun beforeExecuteFun(): Boolean = true
                                override fun executeFun(): Any {
                                    handle.doTaskNotify(definition, instance, task, userIdList)
                                    return true
                                }
                            })
                    }
                }
            } ?: throw WebException("获取流程实例失败：taskId【${task.id}】")
        }
    }
}